package com.log4jviewer.ui.views;

import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IViewSite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.Activator;
import com.log4jviewer.ErrorsContainer;
import com.log4jviewer.domain.LogList;
import com.log4jviewer.filters.FilterModel;
import com.log4jviewer.filters.LogFilterEngine;
import com.log4jviewer.server.Server;
import com.log4jviewer.ui.preferences.DefaultPreferences;
import com.log4jviewer.ui.preferences.ServerPreferences;
import com.log4jviewer.ui.preferences.additional.ColumnModel;
import com.log4jviewer.ui.preferences.additional.LogFieldColumn;

/**
 * Controller for LogView. Loads server settings, manages the updates for LogView table columns.
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class LogViewController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private Server server;

    private ServerPreferences serverPreferences;

    private int serverPort;

    private boolean serverStarted;

    private ErrorsContainer errorsContainer;

    private LogList logList;

    private LogFilterEngine logFilterEngine;

    public void init() {
        logFilterEngine = new LogFilterEngine();
        logList = new LogList(logFilterEngine, getLogBufferSize());
        errorsContainer = new ErrorsContainer();
        serverPreferences = new ServerPreferences();
    }

    public LogList getLogList() {
        return logList;
    }

    public ErrorsContainer getErrorManager() {
        return errorsContainer;
    }

    public boolean isServerStarted() {
        return serverStarted;
    }

    public boolean isServerAlive() {
        boolean isAlive = false;

        if (server != null) {
            isAlive = server.isAlive();
        }
        return isAlive;
    }

    public void setLogFilter(final FilterModel filterModel) {
        logFilterEngine.setFilter(filterModel);
    }

    public void initializeServerSettings() {
        serverPreferences.loadServerPreferences();
        serverPort = serverPreferences.getPort();
        serverStarted = serverPreferences.isStarted();
        logger.debug("Server settings were initialized.");
    }

    private int getLogBufferSize() {
        IPreferenceStore store = Activator.getInstance().getPreferenceStore();
        int logBufferSize = store.getInt(DefaultPreferences.LogViewPreferences.getId());
        return logBufferSize;
    }

    public void updateLogBufferSize() {
        logList.updateLogBufferSize(getLogBufferSize());
    }

    public IAction getStartServerAction(final IViewSite site) {
        IAction startServerAction = ((ActionContributionItem) site.getActionBars()
                .getToolBarManager().find("Log4jViewer.StartServerAction")).getAction();
        return startServerAction;
    }

    public IAction getStopServerAction(final IViewSite site) {
        IAction stopServerAction = ((ActionContributionItem) site.getActionBars()
                .getToolBarManager().find("Log4jViewer.StopServerAction")).getAction();
        return stopServerAction;
    }

    public IAction getErrorViewAction(final IViewSite site) {
        IAction errorViewAction = ((ActionContributionItem) site.getActionBars()
                .getToolBarManager().find("Log4jViewer.ErrorViewAction")).getAction();
        return errorViewAction;
    }

    public void startServer() {
        server = new Server(serverPort, errorsContainer);
        server.addListener(logList);

        if (!server.isAlive()) {
            server.start();
        }
    }

    public void stopServer() {
        if (server != null) {
            server.removeListener(logList);
            server.stopServer();
        }
    }

    public void updateColumnsDisplay(final Table table, final ColumnModel[] columnItems) {
        clearColumnListeners(table);
        int[] columnOrder = table.getColumnOrder();

        for (int i = 0; i < columnItems.length; i++) {
            TableColumn column = table.getColumn(columnOrder[i]);
            logger.debug("Updated column: {}", column.getText());
            showOrHideColumn(columnItems[i], column);

            String columnId = LogFieldColumn.values()[i].getId();
            ColumnListener columnListener = new ColumnListener(column, columnItems[i], columnId);
            column.addListener(SWT.Resize, columnListener);
            saveColumnDisplay(columnItems[i], columnId);
        }
    }

    private void clearColumnListeners(final Table table) {
        // remove all column control listeners before update
        for (int i = 0; i < table.getColumnCount(); i++) {
            TableColumn column = table.getColumn(i);
            Listener[] columnListeners = column.getListeners(SWT.Resize);

            for (Listener columnListener : columnListeners) {
                column.removeListener(SWT.Resize, columnListener);
            }
        }
    }

    public void showOrHideColumn(final ColumnModel columnModel, final TableColumn column) {
        // Hide column if it isn't displayed.
        if (columnModel.isDisplay()) {
            column.setWidth(columnModel.getColumnWidth());
        } else {
            final int hidenColumnWidth = 0;
            column.setWidth(hidenColumnWidth);
        }
    }

    private void saveColumnDisplay(final ColumnModel columnModel, final String columnId) {
        int columnIndex = columnModel.getColumnIndex();
        int columnWidth = columnModel.getColumnWidth();
        boolean isColumnDisplay = columnModel.isDisplay();

        StringBuilder columnPrefs = new StringBuilder();
        columnPrefs.append(columnIndex).append(LogFieldColumn.SEPARATOR).append(columnWidth)
                .append(LogFieldColumn.SEPARATOR).append(isColumnDisplay);

        IPreferenceStore store = Activator.getInstance().getPreferenceStore();
        store.setValue(columnId, columnPrefs.toString());
        logger.debug("Column preferences: ({}) were saved.", columnPrefs);
    }

    public void updateColumns(final Table table, final int[] swapIndexes) {
        final int currentIndexPosistion = 0;
        int currentPosition = swapIndexes[currentIndexPosistion];

        final int nextIndexPosistion = 1;
        int nextPosition = swapIndexes[nextIndexPosistion];

        int[] columnOrder = table.getColumnOrder();
        int tempPosition = columnOrder[nextPosition];
        columnOrder[nextPosition] = columnOrder[currentPosition];
        columnOrder[currentPosition] = tempPosition;
        table.setColumnOrder(columnOrder);
    }

    public void dispose() {
        // Remove all logs from log list
        logList.clear();
        stopServer();
    }

    private class ColumnListener implements Listener {

        private TableColumn column;

        private ColumnModel columnModel;

        private String columnId;

        public ColumnListener(final TableColumn column, final ColumnModel columnModel, final String columnId) {
            this.column = column;
            this.columnModel = columnModel;
            this.columnId = columnId;
        }

        @Override
        public void handleEvent(final Event event) {
            int columnSize = column.getWidth();
            columnModel.setColumnWidth(columnSize);
            saveColumnDisplay(columnModel, columnId);
        }
    }
}
